1 /* 2 * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.management; 27 28 import java.io.IOException; 29 import java.io.StreamCorruptedException; 30 import java.io.Serializable; 31 import java.io.ObjectOutputStream; 32 import java.io.ObjectInputStream; 33 import java.lang.reflect.Method; 34 import java.util.Arrays; 35 import java.util.Map; 36 import java.util.WeakHashMap; 37 import java.security.AccessController; 38 import java.security.PrivilegedAction; 39 40 import static javax.management.ImmutableDescriptor.nonNullDescriptor; 41 42 /** 43 * <p>Describes the management interface exposed by an MBean; that is, 44 * the set of attributes and operations which are available for 45 * management operations. Instances of this class are immutable. 46 * Subclasses may be mutable but this is not recommended.</p> 47 * 48 * <p id="info-changed">Usually the {@code MBeanInfo} for any given MBean does 49 * not change over the lifetime of that MBean. Dynamic MBeans can change their 50 * {@code MBeanInfo} and in that case it is recommended that they emit a {@link 51 * Notification} with a {@linkplain Notification#getType() type} of {@code 52 * "jmx.mbean.info.changed"} and a {@linkplain Notification#getUserData() 53 * userData} that is the new {@code MBeanInfo}. This is not required, but 54 * provides a conventional way for clients of the MBean to discover the change. 55 * See also the <a href="Descriptor.html#immutableInfo">immutableInfo</a> and 56 * <a href="Descriptor.html#infoTimeout">infoTimeout</a> fields in the {@code 57 * MBeanInfo} {@link Descriptor}.</p> 58 * 59 * <p>The contents of the <code>MBeanInfo</code> for a Dynamic MBean 60 * are determined by its {@link DynamicMBean#getMBeanInfo 61 * getMBeanInfo()} method. This includes Open MBeans and Model 62 * MBeans, which are kinds of Dynamic MBeans.</p> 63 * 64 * <p>The contents of the <code>MBeanInfo</code> for a Standard MBean 65 * are determined by the MBean server as follows:</p> 66 * 67 * <ul> 68 * 69 * <li>{@link #getClassName()} returns the Java class name of the MBean 70 * object; 71 * 72 * <li>{@link #getConstructors()} returns the list of all public 73 * constructors in that object; 74 * 75 * <li>{@link #getAttributes()} returns the list of all attributes 76 * whose existence is deduced from the presence in the MBean interface 77 * of a <code>get<i>Name</i></code>, <code>is<i>Name</i></code>, or 78 * <code>set<i>Name</i></code> method that conforms to the conventions 79 * for Standard MBeans; 80 * 81 * <li>{@link #getOperations()} returns the list of all methods in 82 * the MBean interface that do not represent attributes; 83 * 84 * <li>{@link #getNotifications()} returns an empty array if the MBean 85 * does not implement the {@link NotificationBroadcaster} interface, 86 * otherwise the result of calling {@link 87 * NotificationBroadcaster#getNotificationInfo()} on it; 88 * 89 * <li>{@link #getDescriptor()} returns a descriptor containing the contents 90 * of any descriptor annotations in the MBean interface (see 91 * {@link DescriptorKey @DescriptorKey}). 92 * 93 * </ul> 94 * 95 * <p>The description returned by {@link #getDescription()} and the 96 * descriptions of the contained attributes and operations are not specified.</p> 97 * 98 * <p>The remaining details of the <code>MBeanInfo</code> for a 99 * Standard MBean are not specified. This includes the description of 100 * any contained constructors, and notifications; the names 101 * of parameters to constructors and operations; and the descriptions of 102 * constructor parameters.</p> 103 * 104 * @since 1.5 105 */ 106 public class MBeanInfo implements Cloneable, Serializable, DescriptorRead { 107 108 /* Serial version */ 109 static final long serialVersionUID = -6451021435135161911L; 110 111 /** 112 * @serial The Descriptor for the MBean. This field 113 * can be null, which is equivalent to an empty Descriptor. 114 */ 115 private transient Descriptor descriptor; 116 117 /** 118 * @serial The human readable description of the class. 119 */ 120 private final String description; 121 122 /** 123 * @serial The MBean qualified name. 124 */ 125 private final String className; 126 127 /** 128 * @serial The MBean attribute descriptors. 129 */ 130 private final MBeanAttributeInfo[] attributes; 131 132 /** 133 * @serial The MBean operation descriptors. 134 */ 135 private final MBeanOperationInfo[] operations; 136 137 /** 138 * @serial The MBean constructor descriptors. 139 */ 140 private final MBeanConstructorInfo[] constructors; 141 142 /** 143 * @serial The MBean notification descriptors. 144 */ 145 private final MBeanNotificationInfo[] notifications; 146 147 private transient int hashCode; 148 149 /** 150 * <p>True if this class is known not to override the array-valued 151 * getters of MBeanInfo. Obviously true for MBeanInfo itself, and true 152 * for a subclass where we succeed in reflecting on the methods 153 * and discover they are not overridden.</p> 154 * 155 * <p>The purpose of this variable is to avoid cloning the arrays 156 * when doing operations like {@link #equals} where we know they 157 * will not be changed. If a subclass overrides a getter, we 158 * cannot access the corresponding array directly.</p> 159 */ 160 private final transient boolean arrayGettersSafe; 161 162 /** 163 * Constructs an <CODE>MBeanInfo</CODE>. 164 * 165 * @param className The name of the Java class of the MBean described 166 * by this <CODE>MBeanInfo</CODE>. This value may be any 167 * syntactically legal Java class name. It does not have to be a 168 * Java class known to the MBean server or to the MBean's 169 * ClassLoader. If it is a Java class known to the MBean's 170 * ClassLoader, it is recommended but not required that the 171 * class's public methods include those that would appear in a 172 * Standard MBean implementing the attributes and operations in 173 * this MBeanInfo. 174 * @param description A human readable description of the MBean (optional). 175 * @param attributes The list of exposed attributes of the MBean. 176 * This may be null with the same effect as a zero-length array. 177 * @param constructors The list of public constructors of the 178 * MBean. This may be null with the same effect as a zero-length 179 * array. 180 * @param operations The list of operations of the MBean. This 181 * may be null with the same effect as a zero-length array. 182 * @param notifications The list of notifications emitted. This 183 * may be null with the same effect as a zero-length array. 184 */ 185 public MBeanInfo(String className, 186 String description, 187 MBeanAttributeInfo[] attributes, 188 MBeanConstructorInfo[] constructors, 189 MBeanOperationInfo[] operations, 190 MBeanNotificationInfo[] notifications) 191 throws IllegalArgumentException { 192 this(className, description, attributes, constructors, operations, 193 notifications, null); 194 } 195 196 /** 197 * Constructs an <CODE>MBeanInfo</CODE>. 198 * 199 * @param className The name of the Java class of the MBean described 200 * by this <CODE>MBeanInfo</CODE>. This value may be any 201 * syntactically legal Java class name. It does not have to be a 202 * Java class known to the MBean server or to the MBean's 203 * ClassLoader. If it is a Java class known to the MBean's 204 * ClassLoader, it is recommended but not required that the 205 * class's public methods include those that would appear in a 206 * Standard MBean implementing the attributes and operations in 207 * this MBeanInfo. 208 * @param description A human readable description of the MBean (optional). 209 * @param attributes The list of exposed attributes of the MBean. 210 * This may be null with the same effect as a zero-length array. 211 * @param constructors The list of public constructors of the 212 * MBean. This may be null with the same effect as a zero-length 213 * array. 214 * @param operations The list of operations of the MBean. This 215 * may be null with the same effect as a zero-length array. 216 * @param notifications The list of notifications emitted. This 217 * may be null with the same effect as a zero-length array. 218 * @param descriptor The descriptor for the MBean. This may be null 219 * which is equivalent to an empty descriptor. 220 * 221 * @since 1.6 222 */ 223 public MBeanInfo(String className, 224 String description, 225 MBeanAttributeInfo[] attributes, 226 MBeanConstructorInfo[] constructors, 227 MBeanOperationInfo[] operations, 228 MBeanNotificationInfo[] notifications, 229 Descriptor descriptor) 230 throws IllegalArgumentException { 231 232 this.className = className; 233 234 this.description = description; 235 236 if (attributes == null) 237 attributes = MBeanAttributeInfo.NO_ATTRIBUTES; 238 this.attributes = attributes; 239 240 if (operations == null) 241 operations = MBeanOperationInfo.NO_OPERATIONS; 242 this.operations = operations; 243 244 if (constructors == null) 245 constructors = MBeanConstructorInfo.NO_CONSTRUCTORS; 246 this.constructors = constructors; 247 248 if (notifications == null) 249 notifications = MBeanNotificationInfo.NO_NOTIFICATIONS; 250 this.notifications = notifications; 251 252 if (descriptor == null) 253 descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR; 254 this.descriptor = descriptor; 255 256 this.arrayGettersSafe = 257 arrayGettersSafe(this.getClass(), MBeanInfo.class); 258 } 259 260 /** 261 * <p>Returns a shallow clone of this instance. 262 * The clone is obtained by simply calling <tt>super.clone()</tt>, 263 * thus calling the default native shallow cloning mechanism 264 * implemented by <tt>Object.clone()</tt>. 265 * No deeper cloning of any internal field is made.</p> 266 * 267 * <p>Since this class is immutable, the clone method is chiefly of 268 * interest to subclasses.</p> 269 */ 270 @Override 271 public Object clone () { 272 try { 273 return super.clone() ; 274 } catch (CloneNotSupportedException e) { 275 // should not happen as this class is cloneable 276 return null; 277 } 278 } 279 280 281 /** 282 * Returns the name of the Java class of the MBean described by 283 * this <CODE>MBeanInfo</CODE>. 284 * 285 * @return the class name. 286 */ 287 public String getClassName() { 288 return className; 289 } 290 291 /** 292 * Returns a human readable description of the MBean. 293 * 294 * @return the description. 295 */ 296 public String getDescription() { 297 return description; 298 } 299 300 /** 301 * Returns the list of attributes exposed for management. 302 * Each attribute is described by an <CODE>MBeanAttributeInfo</CODE> object. 303 * 304 * The returned array is a shallow copy of the internal array, 305 * which means that it is a copy of the internal array of 306 * references to the <CODE>MBeanAttributeInfo</CODE> objects 307 * but that each referenced <CODE>MBeanAttributeInfo</CODE> object is not copied. 308 * 309 * @return An array of <CODE>MBeanAttributeInfo</CODE> objects. 310 */ 311 public MBeanAttributeInfo[] getAttributes() { 312 MBeanAttributeInfo[] as = nonNullAttributes(); 313 if (as.length == 0) 314 return as; 315 else 316 return as.clone(); 317 } 318 319 private MBeanAttributeInfo[] fastGetAttributes() { 320 if (arrayGettersSafe) 321 return nonNullAttributes(); 322 else 323 return getAttributes(); 324 } 325 326 /** 327 * Return the value of the attributes field, or an empty array if 328 * the field is null. This can't happen with a 329 * normally-constructed instance of this class, but can if the 330 * instance was deserialized from another implementation that 331 * allows the field to be null. It would be simpler if we enforced 332 * the class invariant that these fields cannot be null by writing 333 * a readObject() method, but that would require us to define the 334 * various array fields as non-final, which is annoying because 335 * conceptually they are indeed final. 336 */ 337 private MBeanAttributeInfo[] nonNullAttributes() { 338 return (attributes == null) ? 339 MBeanAttributeInfo.NO_ATTRIBUTES : attributes; 340 } 341 342 /** 343 * Returns the list of operations of the MBean. 344 * Each operation is described by an <CODE>MBeanOperationInfo</CODE> object. 345 * 346 * The returned array is a shallow copy of the internal array, 347 * which means that it is a copy of the internal array of 348 * references to the <CODE>MBeanOperationInfo</CODE> objects 349 * but that each referenced <CODE>MBeanOperationInfo</CODE> object is not copied. 350 * 351 * @return An array of <CODE>MBeanOperationInfo</CODE> objects. 352 */ 353 public MBeanOperationInfo[] getOperations() { 354 MBeanOperationInfo[] os = nonNullOperations(); 355 if (os.length == 0) 356 return os; 357 else 358 return os.clone(); 359 } 360 361 private MBeanOperationInfo[] fastGetOperations() { 362 if (arrayGettersSafe) 363 return nonNullOperations(); 364 else 365 return getOperations(); 366 } 367 368 private MBeanOperationInfo[] nonNullOperations() { 369 return (operations == null) ? 370 MBeanOperationInfo.NO_OPERATIONS : operations; 371 } 372 373 /** 374 * <p>Returns the list of the public constructors of the MBean. 375 * Each constructor is described by an 376 * <CODE>MBeanConstructorInfo</CODE> object.</p> 377 * 378 * <p>The returned array is a shallow copy of the internal array, 379 * which means that it is a copy of the internal array of 380 * references to the <CODE>MBeanConstructorInfo</CODE> objects but 381 * that each referenced <CODE>MBeanConstructorInfo</CODE> object 382 * is not copied.</p> 383 * 384 * <p>The returned list is not necessarily exhaustive. That is, 385 * the MBean may have a public constructor that is not in the 386 * list. In this case, the MBean server can construct another 387 * instance of this MBean's class using that constructor, even 388 * though it is not listed here.</p> 389 * 390 * @return An array of <CODE>MBeanConstructorInfo</CODE> objects. 391 */ 392 public MBeanConstructorInfo[] getConstructors() { 393 MBeanConstructorInfo[] cs = nonNullConstructors(); 394 if (cs.length == 0) 395 return cs; 396 else 397 return cs.clone(); 398 } 399 400 private MBeanConstructorInfo[] fastGetConstructors() { 401 if (arrayGettersSafe) 402 return nonNullConstructors(); 403 else 404 return getConstructors(); 405 } 406 407 private MBeanConstructorInfo[] nonNullConstructors() { 408 return (constructors == null) ? 409 MBeanConstructorInfo.NO_CONSTRUCTORS : constructors; 410 } 411 412 /** 413 * Returns the list of the notifications emitted by the MBean. 414 * Each notification is described by an <CODE>MBeanNotificationInfo</CODE> object. 415 * 416 * The returned array is a shallow copy of the internal array, 417 * which means that it is a copy of the internal array of 418 * references to the <CODE>MBeanNotificationInfo</CODE> objects 419 * but that each referenced <CODE>MBeanNotificationInfo</CODE> object is not copied. 420 * 421 * @return An array of <CODE>MBeanNotificationInfo</CODE> objects. 422 */ 423 public MBeanNotificationInfo[] getNotifications() { 424 MBeanNotificationInfo[] ns = nonNullNotifications(); 425 if (ns.length == 0) 426 return ns; 427 else 428 return ns.clone(); 429 } 430 431 private MBeanNotificationInfo[] fastGetNotifications() { 432 if (arrayGettersSafe) 433 return nonNullNotifications(); 434 else 435 return getNotifications(); 436 } 437 438 private MBeanNotificationInfo[] nonNullNotifications() { 439 return (notifications == null) ? 440 MBeanNotificationInfo.NO_NOTIFICATIONS : notifications; 441 } 442 443 /** 444 * Get the descriptor of this MBeanInfo. Changing the returned value 445 * will have no affect on the original descriptor. 446 * 447 * @return a descriptor that is either immutable or a copy of the original. 448 * 449 * @since 1.6 450 */ 451 public Descriptor getDescriptor() { 452 return (Descriptor) nonNullDescriptor(descriptor).clone(); 453 } 454 455 @Override 456 public String toString() { 457 return 458 getClass().getName() + "[" + 459 "description=" + getDescription() + ", " + 460 "attributes=" + Arrays.asList(fastGetAttributes()) + ", " + 461 "constructors=" + Arrays.asList(fastGetConstructors()) + ", " + 462 "operations=" + Arrays.asList(fastGetOperations()) + ", " + 463 "notifications=" + Arrays.asList(fastGetNotifications()) + ", " + 464 "descriptor=" + getDescriptor() + 465 "]"; 466 } 467 468 /** 469 * <p>Compare this MBeanInfo to another. Two MBeanInfo objects 470 * are equal if and only if they return equal values for {@link 471 * #getClassName()}, for {@link #getDescription()}, and for 472 * {@link #getDescriptor()}, and the 473 * arrays returned by the two objects for {@link 474 * #getAttributes()}, {@link #getOperations()}, {@link 475 * #getConstructors()}, and {@link #getNotifications()} are 476 * pairwise equal. Here "equal" means {@link 477 * Object#equals(Object)}, not identity.</p> 478 * 479 * <p>If two MBeanInfo objects return the same values in one of 480 * their arrays but in a different order then they are not equal.</p> 481 * 482 * @param o the object to compare to. 483 * 484 * @return true if and only if <code>o</code> is an MBeanInfo that is equal 485 * to this one according to the rules above. 486 */ 487 @Override 488 public boolean equals(Object o) { 489 if (o == this) 490 return true; 491 if (!(o instanceof MBeanInfo)) 492 return false; 493 MBeanInfo p = (MBeanInfo) o; 494 if (!isEqual(getClassName(), p.getClassName()) || 495 !isEqual(getDescription(), p.getDescription()) || 496 !getDescriptor().equals(p.getDescriptor())) { 497 return false; 498 } 499 500 return 501 (Arrays.equals(p.fastGetAttributes(), fastGetAttributes()) && 502 Arrays.equals(p.fastGetOperations(), fastGetOperations()) && 503 Arrays.equals(p.fastGetConstructors(), fastGetConstructors()) && 504 Arrays.equals(p.fastGetNotifications(), fastGetNotifications())); 505 } 506 507 @Override 508 public int hashCode() { 509 /* Since computing the hashCode is quite expensive, we cache it. 510 If by some terrible misfortune the computed value is 0, the 511 caching won't work and we will recompute it every time. 512 513 We don't bother synchronizing, because, at worst, n different 514 threads will compute the same hashCode at the same time. */ 515 if (hashCode != 0) 516 return hashCode; 517 518 hashCode = 519 getClassName().hashCode() ^ 520 getDescriptor().hashCode() ^ 521 arrayHashCode(fastGetAttributes()) ^ 522 arrayHashCode(fastGetOperations()) ^ 523 arrayHashCode(fastGetConstructors()) ^ 524 arrayHashCode(fastGetNotifications()); 525 526 return hashCode; 527 } 528 529 private static int arrayHashCode(Object[] array) { 530 int hash = 0; 531 for (int i = 0; i < array.length; i++) 532 hash ^= array[i].hashCode(); 533 return hash; 534 } 535 536 /** 537 * Cached results of previous calls to arrayGettersSafe. This is 538 * a WeakHashMap so that we don't prevent a class from being 539 * garbage collected just because we know whether it's immutable. 540 */ 541 private static final Map<Class<?>, Boolean> arrayGettersSafeMap = 542 new WeakHashMap<Class<?>, Boolean>(); 543 544 /** 545 * Return true if <code>subclass</code> is known to preserve the 546 * immutability of <code>immutableClass</code>. The class 547 * <code>immutableClass</code> is a reference class that is known 548 * to be immutable. The subclass <code>subclass</code> is 549 * considered immutable if it does not override any public method 550 * of <code>immutableClass</code> whose name begins with "get". 551 * This is obviously not an infallible test for immutability, 552 * but it works for the public interfaces of the MBean*Info classes. 553 */ 554 static boolean arrayGettersSafe(Class<?> subclass, Class<?> immutableClass) { 555 if (subclass == immutableClass) 556 return true; 557 synchronized (arrayGettersSafeMap) { 558 Boolean safe = arrayGettersSafeMap.get(subclass); 559 if (safe == null) { 560 try { 561 ArrayGettersSafeAction action = 562 new ArrayGettersSafeAction(subclass, immutableClass); 563 safe = AccessController.doPrivileged(action); 564 } catch (Exception e) { // e.g. SecurityException 565 /* We don't know, so we assume it isn't. */ 566 safe = false; 567 } 568 arrayGettersSafeMap.put(subclass, safe); 569 } 570 return safe; 571 } 572 } 573 574 /* 575 * The PrivilegedAction stuff is probably overkill. We can be 576 * pretty sure the caller does have the required privileges -- a 577 * JMX user that can't do reflection can't even use Standard 578 * MBeans! But there's probably a performance gain by not having 579 * to check the whole call stack. 580 */ 581 private static class ArrayGettersSafeAction 582 implements PrivilegedAction<Boolean> { 583 584 private final Class<?> subclass; 585 private final Class<?> immutableClass; 586 587 ArrayGettersSafeAction(Class<?> subclass, Class<?> immutableClass) { 588 this.subclass = subclass; 589 this.immutableClass = immutableClass; 590 } 591 592 public Boolean run() { 593 Method[] methods = immutableClass.getMethods(); 594 for (int i = 0; i < methods.length; i++) { 595 Method method = methods[i]; 596 String methodName = method.getName(); 597 if (methodName.startsWith("get") && 598 method.getParameterTypes().length == 0 && 599 method.getReturnType().isArray()) { 600 try { 601 Method submethod = 602 subclass.getMethod(methodName); 603 if (!submethod.equals(method)) 604 return false; 605 } catch (NoSuchMethodException e) { 606 return false; 607 } 608 } 609 } 610 return true; 611 } 612 } 613 614 private static boolean isEqual(String s1, String s2) { 615 boolean ret; 616 617 if (s1 == null) { 618 ret = (s2 == null); 619 } else { 620 ret = s1.equals(s2); 621 } 622 623 return ret; 624 } 625 626 /** 627 * Serializes an {@link MBeanInfo} to an {@link ObjectOutputStream}. 628 * @serialData 629 * For compatibility reasons, an object of this class is serialized as follows. 630 * <ul> 631 * The method {@link ObjectOutputStream#defaultWriteObject defaultWriteObject()} 632 * is called first to serialize the object except the field {@code descriptor} 633 * which is declared as transient. The field {@code descriptor} is serialized 634 * as follows: 635 * <ul> 636 * <li> If {@code descriptor} is an instance of the class 637 * {@link ImmutableDescriptor}, the method {@link ObjectOutputStream#write 638 * write(int val)} is called to write a byte with the value {@code 1}, 639 * then the method {@link ObjectOutputStream#writeObject writeObject(Object obj)} 640 * is called twice to serialize the field names and the field values of the 641 * {@code descriptor}, respectively as a {@code String[]} and an 642 * {@code Object[]};</li> 643 * <li> Otherwise, the method {@link ObjectOutputStream#write write(int val)} 644 * is called to write a byte with the value {@code 0}, then the method 645 * {@link ObjectOutputStream#writeObject writeObject(Object obj)} is called 646 * to serialize the field {@code descriptor} directly. 647 * </ul> 648 * </ul> 649 * @since 1.6 650 */ 651 private void writeObject(ObjectOutputStream out) throws IOException { 652 out.defaultWriteObject(); 653 654 if (descriptor.getClass() == ImmutableDescriptor.class) { 655 out.write(1); 656 657 final String[] names = descriptor.getFieldNames(); 658 659 out.writeObject(names); 660 out.writeObject(descriptor.getFieldValues(names)); 661 } else { 662 out.write(0); 663 664 out.writeObject(descriptor); 665 } 666 } 667 668 /** 669 * Deserializes an {@link MBeanInfo} from an {@link ObjectInputStream}. 670 * @serialData 671 * For compatibility reasons, an object of this class is deserialized as follows. 672 * <ul> 673 * The method {@link ObjectInputStream#defaultReadObject defaultReadObject()} 674 * is called first to deserialize the object except the field 675 * {@code descriptor}, which is not serialized in the default way. Then the method 676 * {@link ObjectInputStream#read read()} is called to read a byte, the field 677 * {@code descriptor} is deserialized according to the value of the byte value: 678 * <ul> 679 * <li>1. The method {@link ObjectInputStream#readObject readObject()} 680 * is called twice to obtain the field names (a {@code String[]}) and 681 * the field values (a {@code Object[]}) of the {@code descriptor}. 682 * The two obtained values then are used to construct 683 * an {@link ImmutableDescriptor} instance for the field 684 * {@code descriptor};</li> 685 * <li>0. The value for the field {@code descriptor} is obtained directly 686 * by calling the method {@link ObjectInputStream#readObject readObject()}. 687 * If the obtained value is null, the field {@code descriptor} is set to 688 * {@link ImmutableDescriptor#EMPTY_DESCRIPTOR EMPTY_DESCRIPTOR};</li> 689 * <li>-1. This means that there is no byte to read and that the object is from 690 * an earlier version of the JMX API. The field {@code descriptor} is set to 691 * {@link ImmutableDescriptor#EMPTY_DESCRIPTOR EMPTY_DESCRIPTOR}.</li> 692 * <li>Any other value. A {@link StreamCorruptedException} is thrown.</li> 693 * </ul> 694 * </ul> 695 * @since 1.6 696 */ 697 698 private void readObject(ObjectInputStream in) 699 throws IOException, ClassNotFoundException { 700 701 in.defaultReadObject(); 702 703 switch (in.read()) { 704 case 1: 705 final String[] names = (String[])in.readObject(); 706 707 if (names.length == 0) { 708 descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR; 709 } else { 710 final Object[] values = (Object[])in.readObject(); 711 descriptor = new ImmutableDescriptor(names, values); 712 } 713 714 break; 715 case 0: 716 descriptor = (Descriptor)in.readObject(); 717 718 if (descriptor == null) { 719 descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR; 720 } 721 722 break; 723 case -1: // from an earlier version of the JMX API 724 descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR; 725 726 break; 727 default: 728 throw new StreamCorruptedException("Got unexpected byte."); 729 } 730 } 731 }